介绍
RecyclerView是ListView的豪华增强版。它主要包含以下几处新的特性,如ViewHolder,ItemDecorator,LayoutManager,SmothScroller以及增加或删除item时item动画等。官方推荐我们采用RecyclerView来取代ListView。
相对优势 ViewHolder ListView需要自己实现ViewHolder来提高性能,或者不使用ViewHolder,但是使用ViewHolder来绑定对象是一个很好的习惯。RecyclerView很好的帮我们解决了这个问题,RecyclerView.ViewHolder在使用RecyclerView过程中必须实现,因为它是一个抽象类无法直接创建,需要自己完成对应子类的建立然后使用
LayoutManager ListView只能在垂直方向上滚动,不支持其他的滚动方式,当然开发者有很多自定义的方式完成这些功能,这里就不做争辩,从设计的角度上看,ListView设计之初应该就没有想过让它完成这些复杂的功能,只是为了单纯的列表显示。但是RecyclerView相较于ListView,在滚动上面的功能扩展了许多。它可以支持多种类型列表的展示要求,主要如下:
GridLayoutManager,支持网格展示,可以水平或者竖直滚动,如展示图片的画廊。
LinearLayoutManager ,可以支持水平和竖直方向上滚动的列表。 8 StaggeredGridLayoutManager ,可以支持交叉网格风格的列表,类似于瀑布流或者Pinterest。
ItemAnimation,ItemAnimation是RecyclerView中子项在增加,删除或者移动的情况下显示的动画效果,Google越来越重视用户体验,从属性动画的推出开始,这就是一个趋势,开发者在这里可以自己实现自己想要添加的动画效果,当然,如果你是个懒汉,请使用new DefaultItemAnimator()
ItemDecoration ItemDecoration,名字起的很文艺,子项的装饰,RecyclerView在默认情况下并不在item之间展示间隔符。如果你想要添加间隔符,你必须使用RecyclerView.ItemDecoration类来实现。懒汉请使用DividerItemDecoration.java。
Recycler示例 实际效果图
上面这个效果图是使用的StaggeredGridLayoutManager
基本使用 布局文件 activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:id ="@+id/root_layout" android:layout_width ="match_parent" android:layout_height ="match_parent" android:orientation ="vertical" android:background ="@color/colorMainBackground" > <include layout ="@layout/toolbar" > </include > <FrameLayout xmlns:android ="http://schemas.android.com/apk/res/android" xmlns:tools ="http://schemas.android.com/tools" android:layout_width ="match_parent" android:layout_height ="match_parent" tools:context =".model.View.MainActivity" > <android.support.v7.widget.RecyclerView android:id ="@+id/rv_content" android:layout_width ="match_parent" android:layout_height ="match_parent" > </android.support.v7.widget.RecyclerView > <android.support.design.widget.FloatingActionButton android:id ="@+id/fab_add" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:layout_gravity ="bottom|right" android:layout_marginBottom ="30dp" android:layout_marginRight ="30dp" android:src ="@drawable/addone" /> <android.support.design.widget.FloatingActionButton android:id ="@+id/fab_del" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:layout_gravity ="bottom|left" android:layout_marginBottom ="30dp" android:layout_marginLeft ="30dp" android:src ="@drawable/delone" /> </FrameLayout > </LinearLayout >
recycler_item.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:layout_width ="match_parent" android:layout_height ="match_parent" xmlns:app ="http://schemas.android.com/apk/res-auto" > <android.support.v7.widget.CardView android:layout_margin ="10dp" android:id ="@+id/cv_bg" android:layout_width ="match_parent" android:layout_height ="match_parent" app:cardCornerRadius ="20dp" app:cardElevation ="5dp" > <TextView android:id ="@+id/tv_name" android:layout_width ="match_parent" android:layout_height ="match_parent" android:gravity ="center" /> </android.support.v7.widget.CardView > </FrameLayout >
主Activity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class MainActivity extends AppCompatActivity { @Bind (R.id.rv_content) RecyclerView rvContent; @Bind (R.id.fab_add) FloatingActionButton fabAdd; @Bind (R.id.fab_del) FloatingActionButton fabDel; @Bind (R.id.root_layout) LinearLayout rootLayout; private LayoutManager mLayoutManager; private RVAdapter recyclerAdapter; private ArrayList<String> mContentList; private Random mRandom = new Random(); private int mSum = 50 ; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this ); mContentList = new ArrayList<String>(); for (int i = 0 ; i < mSum; i++) { mContentList.add(getRandomString()); } recyclerAdapter = new RVAdapter(mContentList); mLayoutManager = new StaggeredGridLayoutManager(3 , LinearLayoutManager.VERTICAL); rvContent.setAdapter(recyclerAdapter); rvContent.setLayoutManager(mLayoutManager); rvContent.setItemAnimator(new DefaultItemAnimator()); } @OnClick ({R.id.fab_add, R.id.fab_del}) public void onClick (View view) { switch (view.getId()) { case R.id.fab_add: recyclerAdapter.addData(1 ); makeSnackBar(rootLayout, "添加一个 :)" , null , null ); break ; case R.id.fab_del: recyclerAdapter.removeData(1 ); makeSnakeBar(rootLayout, "删除一个 :(" , null , null ); break ; default : break ; } } public String getRandomString () { String src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" ; StringBuilder dst = new StringBuilder(4 ); for (int i = 0 ; i < 4 ; i++) { dst.append(src.charAt(mRandom.nextInt(62 ))); } return dst.toString(); } private void makeSnackBar (View view, String message, String buttonText, View.OnClickListener onClickListener) { Snackbar.make(view, message, Snackbar.LENGTH_SHORT) .setAction(buttonText, onClickListener) .show(); } }
适配器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class RVAdapter extends RecyclerView .Adapter <RVAdapter .MyViewHolder > { ArrayList<String> mContentList; Random mRandom = new Random(); public class MyViewHolder extends RecyclerView .ViewHolder { TextView textView; CardView cardView; public MyViewHolder (View itemView) { super (itemView); textView = (TextView) itemView.findViewById(R.id.tv_name); cardView = (CardView) itemView.findViewById(R.id.cv_bg); } } public RVAdapter (ArrayList<String> mContentList) { this .mContentList = mContentList; } @Override public MyViewHolder onCreateViewHolder (ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false ); return new MyViewHolder(view); } @Override public void onBindViewHolder (MyViewHolder holder, int position) { holder.cardView.setCardBackgroundColor(getRandomColor()); holder.textView.setText(mContentList.get(position)); holder.itemView.getLayoutParams().height = getRandomHeight(200 ,400 ); } @Override public int getItemCount () { return mContentList.size(); } private int getRandomColor () { return (0xff000000 |mRandom.nextInt(0x00ffffff )); } private int getRandomHeight (int min , int max) { return (mRandom.nextInt(max - min) + min ); } public void addData (int position) { mContentList.add(position, "Insert One" ); notifyItemInserted(position); } public void removeData (int position) { mContentList.remove(position); notifyItemRemoved(position); } }
需要注意的几个地方:
实现自己的ViewHolder继承recyclerView,ViewHolder,因为抽象类不实例化
inflate子项的时候,最后一个参数设置成false
动画效果需要在Adapter中调用notifyItem方法才行 StaggeredGridLayoutManager的效果图上面已经有显示了
其他效果 LinearLayoutManager 1 2 mLayoutManager = new LinearLayoutManager(this ); rvContent.addItemDecoration(new DividerItemDecoration(this , StaggeredGridLayoutManager.VERTICAL));
GridLayoutManager 1 2 mLayoutManager = new GridLayoutManager(this , 3 ); rvContent.addItemDecoration(new DividerItemDecoration(this , StaggeredGridLayoutManager.VERTICAL));
StaggeredGridLayoutManager.HORIZONTAL 1 2 3 4 mLayoutManager = new StaggeredGridLayoutManager(3 , StaggeredGridLayoutManager.HORIZONTAL); RVAdapter.java中onBindViewHolder holder.itemView.getLayoutParams().width = getRandomHeight(200 ,400 );
关于ItemDecoration 这里附上Google Sample中的DividerItemDecoration.java代码 希望可以帮助到大家
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 public class DividerItemDecoration extends RecyclerView .ItemDecoration { private static final int [] ATTRS = new int []{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; public DividerItemDecoration (Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0 ); a.recycle(); setOrientation(orientation); } public void setOrientation (int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation" ); } mOrientation = orientation; } @Override public void onDraw (Canvas c, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical (Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0 ; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child)); final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal (Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0 ; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child)); final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets (Rect outRect, int itemPosition, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { outRect.set(0 , 0 , 0 , mDivider.getIntrinsicHeight()); } else { outRect.set(0 , 0 , mDivider.getIntrinsicWidth(), 0 ); } } }
备注 Android源码中有很多关于这些View的使用方法,大家可以查阅并参考使用。 博文中部分代码:http://download.csdn.net/detail/poorkick/9541326